package net.demilich.metastone.tools; import java.awt.Desktop; import java.io.File; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; import java.util.Collections; import java.util.EnumMap; import java.util.List; import org.apache.commons.lang3.StringUtils; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import javafx.beans.value.ObservableValue; import javafx.collections.FXCollections; import javafx.collections.ObservableList; import javafx.event.ActionEvent; import javafx.fxml.FXML; import javafx.fxml.FXMLLoader; import javafx.scene.control.Button; import javafx.scene.control.CheckBox; import javafx.scene.control.ComboBox; import javafx.scene.control.Label; import javafx.scene.control.RadioButton; import javafx.scene.control.TextField; import javafx.scene.control.ToggleGroup; import javafx.scene.layout.BorderPane; import javafx.scene.layout.Pane; import javafx.stage.FileChooser; import net.demilich.metastone.game.Attribute; import net.demilich.metastone.game.cards.CardSet; import net.demilich.metastone.game.cards.Rarity; import net.demilich.metastone.game.cards.desc.CardDesc; import net.demilich.metastone.game.cards.desc.ParseUtils; import net.demilich.metastone.game.entities.heroes.HeroClass; import net.demilich.metastone.game.spells.desc.SpellDesc; import net.demilich.metastone.gui.common.ComboBoxKeyHandler; class EditorMainWindow extends BorderPane { private static String getCardId(CardDesc card) { String result = ""; String prefix = ""; switch (card.type) { case HERO_POWER: prefix = "hero_power_"; break; case MINION: prefix = card.collectible ? "minion_" : "token_"; break; case SPELL: case CHOOSE_ONE: prefix = "spell_"; break; case WEAPON: prefix = "weapon_"; break; default: break; } for (String word : card.name.split(" ")) { String cleansedWord = word.replace("'", "").replace(":", ""); result += prefix + cleansedWord.toLowerCase(); prefix = "_"; } return result; } @FXML private RadioButton minionRadioButton; @FXML private RadioButton spellRadioButton; @FXML private RadioButton weaponRadioButton; @FXML private TextField nameField; @FXML private Label idLabel; @FXML private TextField descriptionField; @FXML private ComboBox<Rarity> rarityBox; @FXML private ComboBox<HeroClass> heroClassBox; @FXML private ComboBox<CardSet> cardSetBox; @FXML private TextField manaCostField; @FXML private CheckBox collectibleBox; @FXML private Pane contentPanel; @FXML private Button resetButton; @FXML private Button saveButton; private final ToggleGroup cardTypeGroup = new ToggleGroup(); private List<ComboBox<Attribute>> attributeBoxes; private List<TextField> attributeFields; private ICardEditor cardEditor; private CardDesc card; public EditorMainWindow() { FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("/fxml/EditorMainWindow.fxml")); fxmlLoader.setRoot(this); fxmlLoader.setController(this); try { fxmlLoader.load(); } catch (IOException exception) { throw new RuntimeException(exception); } minionRadioButton.setToggleGroup(cardTypeGroup); spellRadioButton.setToggleGroup(cardTypeGroup); weaponRadioButton.setToggleGroup(cardTypeGroup); minionRadioButton.setSelected(true); minionRadioButton.setOnAction(event -> setCardEditor(new MinionCardPanel())); spellRadioButton.setOnAction(event -> setCardEditor(new SpellCardPanel())); nameField.textProperty().addListener(this::onNameChanged); descriptionField.textProperty().addListener(this::onDescriptionChanged); rarityBox.setItems(FXCollections.observableArrayList(Rarity.values())); heroClassBox.setItems(FXCollections.observableArrayList(HeroClass.values())); cardSetBox.setItems(FXCollections.observableArrayList(CardSet.values())); setCardEditor(new MinionCardPanel()); rarityBox.valueProperty().addListener(this::onRarityChanged); resetButton.setOnAction(this::reset); saveButton.setOnAction(this::onSaveButton); heroClassBox.valueProperty().addListener(this::onHeroClassChanged); cardSetBox.valueProperty().addListener(this::onCardSetChanged); collectibleBox.setOnAction(this::onCollectibleChanged); manaCostField.textProperty().addListener(new IntegerListener(value -> card.baseManaCost = value)); attributeBoxes = new ArrayList<>(); attributeFields = new ArrayList<>(); for (int i = 1; i < 99; i++) { @SuppressWarnings("unchecked") ComboBox<Attribute> box = (ComboBox<Attribute>) lookup("#attributeBox" + i); if (box == null) { break; } TextField field = (TextField) lookup("#attributeField" + i); attributeBoxes.add(box); attributeFields.add(field); } setupAttributeBoxes(); } private Object getAttributeValue(String valueString) { Object value = null; if (ParseUtils.tryParseInt(valueString)) { value = Integer.parseInt(valueString); } else if (ParseUtils.tryParseBool(valueString)) { value = Boolean.parseBoolean(valueString); } else { value = valueString; } return value; } private void onAttributesChanged() { card.attributes = new EnumMap<Attribute, Object>(Attribute.class); for (int i = 0; i < attributeBoxes.size(); i++) { ComboBox<Attribute> attributeBox = attributeBoxes.get(i); TextField attributeField = attributeFields.get(i); if (attributeBox.getSelectionModel().getSelectedItem() == null) { continue; } if (StringUtils.isEmpty(attributeField.getText())) { attributeField.setText("true"); } Attribute attribute = attributeBox.getSelectionModel().getSelectedItem(); Object value = getAttributeValue(attributeField.getText()); card.attributes.put(attribute, value); } } private void onCardSetChanged(ObservableValue<? extends CardSet> ov, CardSet oldCardSet, CardSet newCardSet) { card.set = newCardSet; } private void onCollectibleChanged(ActionEvent event) { card.collectible = collectibleBox.isSelected(); } private void onDescriptionChanged(ObservableValue<? extends String> ov, String oldValue, String newValue) { card.description = newValue; } private void onHeroClassChanged(ObservableValue<? extends HeroClass> ov, HeroClass oldHeroClass, HeroClass newHeroClass) { card.heroClass = newHeroClass; } private void onNameChanged(ObservableValue<? extends String> ov, String oldValue, String newValue) { card.name = newValue; card.id = getCardId(card); idLabel.setText(card.id); } private void onRarityChanged(ObservableValue<? extends Rarity> ov, Rarity oldRarity, Rarity newRarity) { card.rarity = newRarity; } private void onSaveButton(ActionEvent event) { save(); } private void reset(ActionEvent event) { for (int i = 0; i < attributeBoxes.size(); i++) { attributeBoxes.get(i).valueProperty().set(null); } card.attributes = null; cardEditor.reset(); } private void save() { FileChooser fileChooser = new FileChooser(); fileChooser.setTitle("Save card"); fileChooser.setInitialDirectory(new File("./cards/")); fileChooser.setInitialFileName(card.id + ".json"); File file = fileChooser.showSaveDialog(getScene().getWindow()); if (file == null) { return; } System.out.println("Saving to: " + file.getName()); GsonBuilder builder = new GsonBuilder().setPrettyPrinting(); builder.disableHtmlEscaping(); builder.registerTypeAdapter(SpellDesc.class, new SpellDescSerializer()); Gson gson = builder.create(); String json = gson.toJson(card); try { // FileUtils.writeStringToFile(file, json); Path path = Paths.get(file.getPath()); Files.write(path, json.getBytes()); Desktop.getDesktop().open(file); } catch (IOException e) { e.printStackTrace(); } } private void setCardEditor(ICardEditor cardEditor) { this.cardEditor = cardEditor; CardDesc newCard = cardEditor.getCardDesc(); if (card != null) { newCard.name = card.name; newCard.description = card.description; newCard.rarity = card.rarity; newCard.heroClass = card.heroClass; newCard.baseManaCost = card.baseManaCost; } else { newCard.name = ""; newCard.rarity = Rarity.FREE; newCard.heroClass = HeroClass.ANY; newCard.set = CardSet.CUSTOM; newCard.baseManaCost = 0; newCard.collectible = true; } card = newCard; card.id = getCardId(card); contentPanel.getChildren().setAll(cardEditor.getPanel()); // update ui nameField.setText(card.name); idLabel.setText(card.id); descriptionField.setText(card.description); rarityBox.getSelectionModel().select(card.rarity); heroClassBox.getSelectionModel().select(card.heroClass); cardSetBox.getSelectionModel().select(card.set); manaCostField.setText(String.valueOf(card.baseManaCost)); collectibleBox.setSelected(card.collectible); } private void setupAttributeBoxes() { for (ComboBox<Attribute> comboBox : attributeBoxes) { ObservableList<Attribute> items = FXCollections.observableArrayList(); items.addAll(Attribute.values()); Collections.sort(items, (obj1, obj2) -> { if (obj1 == obj2) { return 0; } if (obj1 == null) { return -1; } if (obj2 == null) { return 1; } return obj1.toString().compareTo(obj2.toString()); }); comboBox.setItems(items); comboBox.valueProperty().addListener((ov, oldValue, newValue) -> onAttributesChanged()); comboBox.setOnKeyReleased(new ComboBoxKeyHandler<Attribute>(comboBox)); } for (TextField attributeField : attributeFields) { attributeField.textProperty().addListener((ov, oldValue, newValue) -> onAttributesChanged()); } } }